#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netdb.h>
#include <unistd.h>
#include <fcntl.h>

#include "mw_config.h"
#include "mw_core.h"

int tcp_listen(const char *host, const char *serv)
{
    int fd = 0, on = 1;
    struct addrinfo ai, *pa;
    memset(&ai, 0, sizeof(struct addrinfo));
    ai.ai_family = AF_INET;
    ai.ai_socktype = SOCK_STREAM;
    ai.ai_flags = AI_PASSIVE;

    if (getaddrinfo(host, serv, &ai, &pa) != 0) 
        return -1; 

    if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) 
        return -1; 

    setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, sizeof(on));
    setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on));

    if (fcntl(fd, F_SETFD, 1) == -1) 
    {
        freeaddrinfo(pa);
        return -1; 
    }

    if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) 
    {
        freeaddrinfo(pa);
        return -1; 
    }

    if (bind(fd, pa->ai_addr, pa->ai_addrlen) || listen(fd, 1024)) 
    {
        freeaddrinfo(pa);
        close(fd);
        return -1; 
    }   

    freeaddrinfo(pa);
    return fd; 
} 

/*
 * ioctl(FIONBIO) sets a non-blocking mode with the single syscall
 * while fcntl(F_SETFL, O_NONBLOCK) needs to learn the current state
 * using fcntl(F_GETFL).
 *
 * ioctl() and fcntl() are syscalls at least in FreeBSD 2.x, Linux 2.2
 * and Solaris 7.
 *
 * ioctl() in Linux 2.4 and 2.6 uses BKL, however, fcntl(F_SETFL) uses it too.
 */


int mw_nonblocking(int s)
{
    int  nb;

    nb = 1;

    return ioctl(s, FIONBIO, &nb);
}


int mw_blocking(int s)
{
    int  nb;

    nb = 0;

    return ioctl(s, FIONBIO, &nb);
}


#if (__FreeBSD__)

int mw_tcp_nopush(int s)
{
    int  tcp_nopush;

    tcp_nopush = 1;

    return setsockopt(s, IPPROTO_TCP, TCP_NOPUSH,
                      (const void *) &tcp_nopush, sizeof(int));
}


int mw_tcp_push(int s)
{
    int  tcp_nopush;

    tcp_nopush = 0;

    return setsockopt(s, IPPROTO_TCP, TCP_NOPUSH,
                      (const void *) &tcp_nopush, sizeof(int));
}

#elif (__linux__)


int mw_tcp_nopush(int s)
{
    int  cork;

    cork = 1;

    return setsockopt(s, IPPROTO_TCP, TCP_CORK,
                      (const void *) &cork, sizeof(int));
}


int mw_tcp_push(int s)
{
    int  cork;

    cork = 0;

    return setsockopt(s, IPPROTO_TCP, TCP_CORK,
                      (const void *) &cork, sizeof(int));
}

#else

int mw_tcp_nopush(int s)
{
    return 0;
}


int mw_tcp_push(int s)
{
    return 0;
}

#endif
